package main

import(
	"net"
	"time"
	"fmt"
	"os"
)


const FD_VERBOSE = 0;

const FD_MAXPEERS=4;
var FD_num_peers int;
var FD_peers [FD_MAXPEERS] string;
var FD_status[FD_MAXPEERS] int;
const PING_INTERVAL=2*1e9;
//const LISTEN_READ_TIMEOUT=20e9;
const READ_TIMEOUT=5e9;

type status_update struct {
	IP string;
	status string;
}

/*
func main() {
	peers :=[]string{"127.0.0.2","127.0.0.3", "127.0.0.4"};
	go failureDetector(peers,3);
	for ;; {
		time.Sleep(10*1e9);
	}
}*/

func failureDetector(peersinfo [] string,  numPeers int){
	initPeers(peersinfo, numPeers);
	test();
	go startListener();
	startPinger();
}

//copy peer information, initialize status as unreachable
func initPeers(peers_arr []string, numpeers int){

	if(numpeers > FD_MAXPEERS){
		fmt.Print("FD Number of peers = ",numpeers,", which is >  ",FD_MAXPEERS,"- Expect funny behavior & crashes\n");
	}	

	for i:=0;i<numpeers;i++{
		FD_peers[i] = peers_arr[i];
		FD_status[i]=0;
	}
	FD_num_peers = numpeers;
}

//useless function
func test(){
	if(FD_VERBOSE == 0 ){
		return;
	}	

	fmt.Print("FD Number of peers:",FD_num_peers,"\n");
	for i:=0;i<FD_num_peers;i++{
		fmt.Print("FD Added Peer: ",FD_peers[i],"\n");
	}
}

//Reply to heartbeat pings
func startListener(){

	var ad net.TCPAddr;
	//ad.IP=net.ParseIP("127.0.0.1");
	ad.IP=net.ParseIP("localhost");
	ad.Port= 9999;

	if(FD_VERBOSE !=0){
		fmt.Print("FD Listening for pings\n");
	}
	list,e:=net.ListenTCP("tcp", &ad);
	if(e!=nil){fmt.Print("FD listener error ",e.String());}

	// Accept all connections, and handle it.
	for ;; {
		//Listen for incoming connections
		conn,e:=list.AcceptTCP();

		conn.Close();
		if(e!=nil){
			fmt.Print("FD Listener accept error: " ,e.String());
		} else {

			if(FD_VERBOSE !=0){
				fmt.Print("FD new connection with ",conn.RemoteAddr().String(),"\n");
			}	
		}
		//go handleConnection(conn);

	}
}

func handleConnection(conn * net.TCPConn){
/*	var in[4] byte;
	var out[3] byte;
	out[0]=2;
//	conn.SetReadTimeout(LISTEN_READ_TIMEOUT);
	for ;; {
		in[0]=0;
		x,e:=conn.Read(&in);
		if(e!=nil){
			fmt.Print("FD Listener rcvd ",x," bytes. Error:",e.String()," from ",conn.RemoteAddr(),"\n");
			conn.Close();
			return;
		}
		//fmt.Print("Read ",x,"bytes\n");

		if(in[0]==1){
			conn.Write(&out);
		} else {
			fmt.Print("FD got bad value ",in,"from",conn.RemoteAddr().String(),"\n");			
		}
	}*/
	conn.Close();
}

// For each peer, set or unset status- according to whether u receive reply or not
func startPinger (){
	var connections [FD_MAXPEERS] * net.TCPConn ;
	var ad net.TCPAddr;
	var err os.Error;
	var out[3] byte;
	var in[3] byte;
	//var n int;

	out[0]=1;

	if(FD_VERBOSE !=0){fmt.Printf("FD pinger started\n");}
	for ;; {

		in[0]=0;
		//In a round robin fashion, ping peers
		for i:=0;i<FD_num_peers;i++{

			//If the peer is down, try to establish connection 
			
				ad.IP=net.ParseIP(FD_peers[i]);
				ad.Port = 9999;
				connections[i],err = net.DialTCP("tcp",nil,&ad);
		//		connections[i].SetReadTimeout(READ_TIMEOUT);

				//If machine comes up, try pinging & indicate status change to PM
				if(err == nil && FD_status[i]==0){
							if(FD_VERBOSE !=0){
							fmt.Print("FD ",FD_peers[i]," is up\n");
							}
							newstatus := new(status_update);
							newstatus.IP= FD_peers[i];
							newstatus.status = "up";
							failure_data_chan  <- *newstatus;
							FD_status[i] = 1;


							if(FD_VERBOSE !=0){
								fmt.Print("FD ----------status----------\n");
								for j:=0 ; j<FD_num_peers ; j++ {
									fmt.Print(FD_peers[j],"  ",FD_status[j],"\n");
								}
								fmt.Print("   --------------------------\n\n");
							}	
							connections[i].Close();
						}
					if(err != nil && FD_status[i]==1){
							if(FD_VERBOSE !=0){
							fmt.Print("FD ",FD_peers[i]," is down\n");
							}
							newstatus := new(status_update);
							newstatus.IP= FD_peers[i];
							newstatus.status = "down";
							failure_data_chan  <- *newstatus;
							FD_status[i] = 0;


							if(FD_VERBOSE !=0){
								fmt.Print("FD ----------status----------\n");
								for j:=0 ; j<FD_num_peers ; j++ {
									fmt.Print(FD_peers[j],"  ",FD_status[j],"\n");
								}
								fmt.Print("   --------------------------\n\n");
							}	
							connections[i].Close();
						}
				
						connections[i].Close();		
				
		}
		time.Sleep(PING_INTERVAL);
	}
}

func getCurrentState () (current_status [FD_MAXPEERS]status_update){
	for i:=0;i<FD_num_peers;i++{
		current_status[i].IP=FD_peers[i];
		if(FD_status[i] == 1){
			current_status[i].status="up";
		} else{
			current_status[i].status="down";
		}	
	}
	return current_status;
}
